home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
IRIS Performer 2.2 Friends Demo
/
SGI IRIS Performer 2.2 Friends Demo.iso
/
friends
/
medit
/
pfLoader
/
Internalstuff
/
read.c
< prev
next >
Wrap
Text File
|
1997-11-20
|
24KB
|
757 lines
/************************************************************************
Read a Model file
************************************************************************/
/************************************************************************
Materials
************************************************************************/
static void ReadMaterial(reg FILE *f, reg MaterialPtr m)
{
int token;
char name[MaxName];
while (GetToken(f, &token)) {
switch (token) {
case BlockEnd: return;
case FMName: GetStuff(GetString(f, name, MaxName));
RenameMaterial(m, name); break;
case FMAmbient: GetStuff(GetFloats(f, m->Ambient, 3)); break;
case FMDiffuse: GetStuff(GetFloats(f, m->Diffuse, 3)); break;
case FMSpecular: GetStuff(GetFloats(f, m->Specular, 3)); break;
case FMEmissive: GetStuff(GetFloats(f, m->Emissive, 3)); break;
case FMShine: GetStuff(GetFloats(f, &m->Shine, 1)); break;
case FMAlpha: GetStuff(GetFloats(f, &m->Alpha, 1)); break;
default: if (!SkipFuture(f)) {
SetError(BAD_MATERIAL);
}
}
}
}
static void ReadMaterials(reg FILE *f)
{
MaterialPtr m;
int i, index, token;
while (GetToken(f, &token)) {
switch (token) {
case BlockEnd: return;
case FMat: m = NewMaterial();
if (Compatibility < 3.02) {
GetStuff(GetByte(f, &index));
}
else {
GetStuff(GetInt(f, &index));
}
if (index >= MatListSize) {
int newsize = index+100;
if (MatListSize IS 0) {
MaterialList = malloc(newsize*sizeof(MaterialPtr));
}
else {
MaterialList = realloc(MaterialList, newsize*sizeof(MaterialPtr));
}
for (i=MatListSize; i<newsize; i++) {
MaterialList[i] = NULL;
}
MatListSize = newsize;
}
MaterialList[index] = m;
ReadMaterial(f,m);
AddMaterial(m);
break;
default: if (!SkipFuture(f)) {
SetError(BAD_MATERIAL);
}
}
}
}
/************************************************************************
Read a texture reference
************************************************************************/
static void ReadTextureRef(reg FILE *f, flag ReadAttributes)
{
reg int i;
float filter;
reg TexturePtr t;
char name[MaxFileName];
int index, token, size, fast, minfilter, magfilter, clampx, clampy, transparent, alphatest;
GetStuff(GetInt(f, &index));
GetStuff(GetString(f, name, MaxFileName));
if (index >= TexListSize) {
int newsize = index+100;
if (TexListSize IS 0) {
TextureList = malloc(newsize*sizeof(TexturePtr));
}
else {
TextureList = realloc(TextureList, newsize*sizeof(TexturePtr));
}
for (i=TexListSize; i<newsize; i++) {
TextureList[i] = NULL;
}
TexListSize = newsize;
}
t = NewTexture();
t->File = CopyOfString(name);
AddTexture(t);
TextureList[index] = t;
minfilter = MT_NULL; /* Default values for texture attributes */
magfilter = MT_NULL;
fast = FALSE;
clampx = FALSE;
clampy = FALSE;
transparent = FALSE;
alphatest = 25;
while (ReadAttributes AND GetToken(f, &token)) {
switch (token) {
case BlockEnd: ReadAttributes = FALSE;
break;
case FMinFilter: if (Compatibility < 3.00) {
GetStuff(GetSize(f,&size));
}
GetStuff(GetFloats(f,&filter,1));
minfilter = (int)filter;
break;
case FMagFilter: if (Compatibility < 3.00) {
GetStuff(GetSize(f,&size));
}
GetStuff(GetFloats(f,&filter,1));
minfilter = (int)filter;
break;
case FFastTex: if (Compatibility < 3.00) {
GetStuff(GetSize(f,&size));
}
GetStuff(GetInt(f, &fast));
break;
case FOldClamp: GetStuff(GetSize(f, &size));
GetStuff(GetInt(f, &clampx));
clampy = clampx;
break;
case FClampTexX: GetStuff(GetInt(f, &clampx));
break;
case FClampTexY: GetStuff(GetInt(f, &clampy));
break;
case FTexTransparent:GetStuff(GetInt(f, &transparent));
break;
case FTexAlphaTest: GetStuff(GetInt(f, &alphatest));
break;
default: if (!SkipFuture(f)) {
SetError(BAD_TEXTURE);
}
}
}
if (t) {
t->MinFilter = minfilter;
t->MagFilter = magfilter;
t->Fast = fast;
t->ClampX = clampx;
t->ClampY = clampy;
t->Transparent = transparent;
t->AlphaTest = alphatest;
}
}
/************************************************************************
Read a vertex table if present in the file
************************************************************************/
static int VertexTableSize = 0;
static real (*VertexTable)[XYZ] = NULL;
static void ReadVertexTable(reg FILE *f)
{
int size;
GetStuff(GetInt(f, &size));
if (size > VertexTableSize) {
VertexTableSize = size + 1000;
if (!VertexTable) {
VertexTable = malloc(VertexTableSize * sizeof(real) * XYZ);
}
else {
VertexTable = realloc(VertexTable, VertexTableSize * sizeof(real) * XYZ);
}
}
GetReals(f, (real*)VertexTable, size*XYZ);
}
/************************************************************************
Read a vertex
************************************************************************/
static void ReadVertex(reg FILE *f, PolygonPtr p)
{
real c[XYZ];
int token, index;
reg PolygonBeadPtr pb = NULL;
while (GetToken(f, &token)) {
switch (token) {
case BlockEnd: pb->Owner = p;
return;
case FVIndex: GetStuff(GetInt(f, &index));
pb = AddPolygonBead(&(p->FirstBead), VertexTable[index]);
AddToVertexList(pb);
pb->Colour = LastVertexColour;
break;
case FVCoord: GetStuff(GetReals(f, c, XYZ));
pb = AddPolygonBead(&(p->FirstBead), c);
AddToVertexList(pb);
pb->Colour = LastVertexColour;
break;
case FVTexture: GetStuff(GetFloats(f, pb->TextureCoord, XY));
break;
case FVNormal: GetStuff(GetFloats(f, pb->Normal, XYZ));
break;
case FVColour: GetStuff(GetInt(f, (int*)&LastVertexColour));
pb->Colour = LastVertexColour;
break;
default: if (!SkipFuture(f)) {
SetError(BAD_VERTEX);
}
}
}
}
/************************************************************************
Read a polygon
************************************************************************/
static void ReadPolygon(reg FILE *f, reg TreePtr t, PolygonPtr parent)
{
int token,data;
reg PolygonPtr p;
p = AddPolygon(NULL, t, parent);
AddToPolygonList(p);
p->LightGroup = 0;
p->Texture = LastTexture;
p->Colour = LastPolyColour;
p->Material = LastMaterial;
p->Smooth = p->Coloured = p->Envmapped = FALSE;
while (GetToken(f, &token)) {
switch (token) {
case BlockEnd: if (Compatibility < 402) {
p->Coloured = (p->Material IS NULL);
}
if (!p->Smooth) {
reg PolygonBeadPtr pb;
reg float x = p->Normal[X];
reg float y = p->Normal[Y];
reg float z = p->Normal[Z];
LoopThroughPolygonsBeads(p, pb) {
pb->Normal[X] = x;
pb->Normal[Y] = y;
pb->Normal[Z] = z;
}
}
return;
case FPolygon: ReadPolygon(f, t, p);
break;
case FPSmooth: p->Smooth = TRUE;
break;
case FPColoured: p->Coloured = TRUE;
break;
case FPMaterial: GetStuff(GetInt(f, &data));
if ((data < 0) OR (data > MatListSize)) {
if ((data ISNT -1) OR (data > TexListSize)) {
fprintf(stderr, "%s file: bad material index!\n", ProgName);
}
LastMaterial = NULL;
}
else {
LastMaterial = MaterialList[data];
}
p->Material = LastMaterial;
break;
case FPColour: GetStuff(GetInt(f, (int*)&LastPolyColour));
p->Colour = LastPolyColour;
break;
case FPNormal: GetStuff(GetFloats(f, p->Normal, XYZ));
break;
case FPTexture: GetStuff(GetInt(f, &data));
if ((data < 0) OR (data > TexListSize)) {
if ((data ISNT -1) OR (data > TexListSize)) {
fprintf(stderr, "%s file: bad texture index!\n", ProgName);
}
LastTexture = NULL;
}
else {
LastTexture = TextureList[data];
}
p->Texture = LastTexture;
break;
case FPEnvmapped: p->Envmapped = TRUE;
break;
case FPLightGroup: GetStuff(GetInt(f, &(p->LightGroup)));
break;
case FVertex: ReadVertex(f,p);
break;
default: if (!SkipFuture(f)) {
SetError(BAD_POLYGON);
}
}
}
}
/************************************************************************
Read an object's hierarchy data
************************************************************************/
static void ReadHierarchy(reg FILE *f, reg TreePtr currentbranch, ModelObjectPtr o)
{
int token;
reg TreePtr new;
flag done, across;
char name[MaxName];
new = NULL;
done = FALSE;
across = FALSE;
until (done OR !GetToken(f, &token)) {
switch (token) {
case FNewBranch: VertexIndex = 0;
GetStuff(GetString(f,name,MaxName)); break;
case FBranchId: GetStuff(GetInt(f, &(currentbranch->BranchId))); break;
case FPushBranch: across = TRUE; break;
case FPopBranch: currentbranch = GoUp(o->Tree, currentbranch);
done = (currentbranch IS NULL); break;
case FGroupBranch: new = NewTreeBranch(GroupBranch, name); break;
case FPolygonBranch: new = NewTreeBranch(PolygonBranch, name); break;
case FLodBranch: new = NewTreeBranch(LodBranch, name); break;
case FInstanceBranch: new = NewTreeBranch(InstanceBranch, name); break;
case FSwitchBranch: new = NewTreeBranch(SwitchBranch, name); break;
case FDcsBranch: new = NewTreeBranch(DcsBranch, name); break;
case FVRMLBranch: new = NewTreeBranch(VRMLBranch, name); break;
case FSolidBranch: new = NewTreeBranch(SolidBranch, name); break;
case FBranchSubType: GetStuff(GetInt(f, &(currentbranch->SubType))); break;
case FBranchEyeOpen: GetStuff(GetByte(f, &(currentbranch->EyeOpen))); break;
case FBranchFolded: GetStuff(GetByte(f, &(currentbranch->Folded))); break;
case FBranchVisible: GetStuff(GetByte(f, &(currentbranch->Visible))); break;
case FVertexTable: ReadVertexTable(f); break;
case FPolygon: ReadPolygon(f, currentbranch, NULL); break;
case FBackface: GetStuff(GetInt(f, &(currentbranch->Backfaced))); break;
case FBillboard: GetStuff(GetInt(f, &(currentbranch->Billboard))); break;
case FShareNormals: GetStuff(GetInt(f, &(currentbranch->ShareNormals))); break;
case FBoundingBox: ReadBoundingBox(f, currentbranch->Min, currentbranch->Max); break;
case FTmesh: ReadTmesh(f, currentbranch); break;
case FUnmeshed: ReadUnmeshed(f, currentbranch); break;
case FVertexJoin: ReadVertexJoin(f, currentbranch); break;
case FSwitchDistance: GetStuff(GetReals(f, &(currentbranch->SwitchOut), 1)); break;
case FInstance: GetStuff(GetInt(f, &(currentbranch->ObjId))); break;
case FVRMLIsInline: GetStuff(GetInt(f, &(currentbranch->IsInline))); break;
case FVRMLInlineName: GetStuff(GetString(f,name,MaxName));
currentbranch->InlineName = CopyOfString(name); break;
case FMatrix: GetStuff(GetMatrix(f, currentbranch->Transformation)); break;
case FDcsMatrix: GetStuff(GetMatrix(f, currentbranch->DcsMatrix)); break;
case FHoldValues: GetStuff(GetSizedInt(f, ¤tbranch->HoldValues)); break;
case FVRMLAnchorName: GetStuff(GetString(f,name,MaxName));
currentbranch->VRMLAnchor = CopyOfString(name); break;
case FSeqTime: GetStuff(GetReals(f, &(currentbranch->SeqTime), 1)); break;
case FSeqType: GetStuff(GetInt(f, &(currentbranch->SeqType))); break;
case FScale: GetStuff(GetReals(f, &(currentbranch->Scale), 1)); break;
default: if (!SkipFuture(f)) {
SetError(BAD_OBJECT);
}
}
if (new) {
if (currentbranch) {
if (across) {
currentbranch->Across = new;
#if ITSTHEMODELLER
new->Back = currentbranch;
#endif
across = FALSE;
}
else {
currentbranch->Down = new;
#if ITSTHEMODELLER
new->Up = currentbranch;
#endif
}
}
else {
o->Tree = new;
}
currentbranch = new;
new = NULL;
}
}
}
/************************************************************************
Read an object's engines
************************************************************************/
static void ReadEngines(reg FILE *f, reg TreePtr currentbranch, ModelObjectPtr o)
{
int token;
reg TreePtr new;
flag done, across;
char name[MaxName];
new = NULL;
done = FALSE;
across = FALSE;
until (done OR !GetToken(f, &token)) {
switch (token) {
case FNewEngine: GetStuff(GetString(f,name,MaxName)); break;
case FBranchId: GetStuff(GetInt(f, &(currentbranch->BranchId))); break;
case FPushEngine: across = TRUE; break;
case FPopEngine: currentbranch = GoUp(o->Engines, currentbranch);
done = (currentbranch IS NULL); break;
case FGroupBranch: new = NewTreeBranch(GroupBranch, name); break;
case FActivator: new = NewTreeBranch(ActivatorEngine, name); break;
case FDeactivator: new = NewTreeBranch(DeactivatorEngine, name); break;
case FTimeEngine: new = NewTreeBranch(TimeEngine, name); break;
case FLineEngine: new = NewTreeBranch(LineEngine, name); break;
case FAngleEngine: new = NewTreeBranch(AngleEngine, name); break;
case FWaveEngine: new = NewTreeBranch(WaveEngine, name); break;
case FSensorEngine: new = NewTreeBranch(SensorEngine, name); break;
case FScalarEngine: new = NewTreeBranch(ScalarEngine, name); break;
case FSplineEngine: new = NewTreeBranch(SplineEngine, name); break;
case FCompareEngine: new = NewTreeBranch(CompareEngine, name); break;
case FMatrixEngine: new = NewTreeBranch(GroupBranch, name); break;
case FSwitchOutput: new = NewTreeBranch(SwitchOutput, name); break;
case FDcsOutput: new = NewTreeBranch(DcsOutput, name); break;
case FConnectTo: GetStuff(GetInt(f, &(currentbranch->ConnectId))); break;
case FBranchFolded: GetStuff(GetByte(f, &(currentbranch->Folded))); break;
case FStartActive: GetStuff(GetByte(f, &(currentbranch->StartActive))); break;
case FOutputX: GetStuff(GetByte(f, &(currentbranch->OutputX))); break;
case FOutputY: GetStuff(GetByte(f, &(currentbranch->OutputY))); break;
case FOutputZ: GetStuff(GetByte(f, &(currentbranch->OutputZ))); break;
case FOutputH: GetStuff(GetByte(f, &(currentbranch->OutputH))); break;
case FOutputP: GetStuff(GetByte(f, &(currentbranch->OutputP))); break;
case FOutputR: GetStuff(GetByte(f, &(currentbranch->OutputR))); break;
case FNoWaves: GetStuff(GetReals(f, &(currentbranch->NoWaves), 1)); break;
case FWaveTime: GetStuff(GetReals(f, &(currentbranch->WaveTime), 1)); break;
case FScale: GetStuff(GetReals(f, &(currentbranch->Scale), 1)); break;
case FOffset: GetStuff(GetReals(f, &(currentbranch->Offset), 1)); break;
case FPhase: GetStuff(GetReals(f, &(currentbranch->Phase), 1)); break;
case FSwitchOn: GetStuff(GetInt(f, &(currentbranch->SwitchOn))); break;
case FMotionPath: ReadPathData(f, &(currentbranch->PathData)); break;
case FCompareType: GetStuff(GetSizedInt(f, ¤tbranch->CompareType)); break;
case FHoldValues: GetStuff(GetSizedInt(f, ¤tbranch->HoldValues)); break;
/* Old stuff for REALAX */
case FKey: GetStuff(SkipData(f, 1)); break;
case FXConMode: case FXMinValue: case FXMaxValue:
case FYConMode: case FYMinValue: case FYMaxValue:
case FZConMode: case FZMinValue: case FZMaxValue:
case FHConMode: case FHMinValue: case FHMaxValue:
case FPConMode: case FPMinValue: case FPMaxValue:
case FRConMode: case FRMinValue: case FRMaxValue:
GetStuff(SkipData(f, 4)); break;
default: if (!SkipFuture(f)) {
SetError(BAD_OBJECT);
}
}
if (new) {
if (currentbranch) {
if (across) {
currentbranch->Across = new;
#if ITSTHEMODELLER
new->Back = currentbranch;
#endif
across = FALSE;
}
else {
currentbranch->Down = new;
#if ITSTHEMODELLER
new->Up = currentbranch;
#endif
}
}
else {
o->Engines = new;
}
currentbranch = new;
new->ConnectId = -1;
new = NULL;
}
}
}
/************************************************************************
Read an object
************************************************************************/
static void ReadObject(reg FILE *f)
{
real flyspeed;
ModelObjectPtr o;
char name[MaxName];
char exref[MaxFileName];
int token, index, steps, attributes;
flag GotSubObject = FALSE, GotLod = FALSE;
GetString(f,name,MaxName);
o = AddObject(name);
LastTexture = NULL;
LastMaterial = NULL;
LastPolyColour = LastVertexColour = DefaultColour;
index = 9;
steps = 10;
flyspeed = 1.0;
attributes = 0;
while (GetToken(f, &token)) {
switch (token) {
case BlockEnd: if (Compatibility < 400) {
FreeSubObjects();
RemoveEmptyLods(o);
if (attributes & 1) {
SetBillboarding(o->Tree);
}
if (!GotSubObject OR !GotLod) {
SetError(BAD_FILE);
}
}
#if ITSTHEMODELLER
else {
GenerateObjectStats();
}
o->Connected = FALSE; /* Recalculate bounding boxes... */
o->FlySpeed = flyspeed;
o->GridIndex = index;
o->GridSteps = steps;
#endif
return;
case FPushBranch: ReadHierarchy(f, NULL, o); break;
case FPushEngine: ReadEngines(f, NULL, o); break;
case FObjectId: GetStuff(GetInt(f, &(o->Id))); break;
case FIsTopLevel: GetStuff(GetInt(f, &(o->IsTopLevel))); break;
case FExrefFile: GetStuff(GetString(f, exref, MaxFileName));
o->File = CopyOfString(exref);
ReadExref(o); break;
case FView: ReadView(f); break;
case FGridIndex: GetStuff(GetInt(f, &index)); break;
case FFlySpeed: GetStuff(GetReals(f, &flyspeed, 1)); break;
case FGridSteps: GetStuff(GetInt(f, &steps)); break;
case FBoundingBox: ReadBoundingBox(f, o->Min, o->Max); break;
case FVConstruction:ReadConstructionVertex(f, o); break;
/* Obsolete but needed for backwards compatibility */
case FVList: ReadOldVertices(f); break;
case FSubObject: ReadSubObject(f); GotSubObject = TRUE; break;
case FLod: ReadLod(f, o); GotLod = TRUE; break;
case FObjAttr: GetStuff(GetInt(f,&attributes)); break;
/* Unknown */
default: if (!SkipFuture(f)) {
SetError(BAD_FILE);
}
}
}
}
/************************************************************************
Read data items...
************************************************************************/
static void ReadDataItem(FILE *f)
{
int item, size, cid, n;
GetStuff(GetByte(f, &item));
GetStuff(GetSize(f, &size));
#if ITSTHEMODELLER
if (item IS DSceneGridIndex) {
GetStuff(GetInt(f,&n));
RememberSceneGridIndex(n);
}
else if (item IS DSceneGridSteps) {
GetStuff(GetInt(f,&n));
RememberSceneGridSteps(n);
}
else {
if (ReadingExternal) {
SkipData(f,size);
}
else switch (item) {
case DCurrentObject: GetStuff(GetInt(f,&FileObjectId)); break;
case DNoViewports: GetStuff(GetInt(f,&n));
if (n ISNT NoViewports) {
ChangeNoViewports();
} break;
case DObjectIsolated: GetStuff(GetInt(f,&n));
if ((Compatibility < 400) AND !n) {
FileObjectId = -1;
} break;
case DAnimateWhenMoving: GetStuff(GetInt(f,&AnimateWhenMoving)); break;
case DEditingEngines: GetStuff(GetInt(f,&ShowEngines)); break;
case DFileCreateId: GetStuff(GetInt(f,&cid));
FileCreateId = cid; break;
case DFilePublicDomain: GetStuff(GetInt(f,&FilePublicDomain)); break;
case DFileTitle: GetString(f,FileTitle,CommentSize); break;
case DFileAuthor: GetString(f,FileAuthor,CommentSize); break;
case DFileCompany: GetString(f,FileCompany,CommentSize); break;
case DFileContact: GetString(f,FileContact,CommentSize); break;
case DFileComment1: GetString(f,FileComment1,CommentSize); break;
case DFileComment2: GetString(f,FileComment2,CommentSize); break;
case DFileComment3: GetString(f,FileComment3,CommentSize); break;
case DFileComment4: GetString(f,FileComment4,CommentSize); break;
/* Unknown in this revision, throw it away */
default: SkipData(f,size);
}
}
#else
if (ReadingExternal) {
SkipData(f,size);
}
else {
switch (item) {
case DOriginalFilePath: GetString(f, mwOriginalPath, MaxFileName);
break;
default: SkipData(f,size);
}
}
#endif
}
/************************************************************************
Read a model file...
************************************************************************/
#define HeaderMax 200
boolean ReadModel(char *fname)
{
reg int i;
reg FILE *f;
reg flag done;
int type, token;
reg ModelObjectPtr o;
char header[HeaderMax];
#if !ITSTHEMODELLER
char *path, *name, n[MaxFileName];
#endif
done = FALSE;
DefineTokens();
OldScene = NULL;
#if !ITSTHEMODELLER
mwOriginalPath[0] = '\0';
#endif
for (i=0; i<MatListSize; i++) {
MaterialList[i] = NULL;
}
for (i=0; i<TexListSize; i++) {
TextureList[i] = NULL;
}
if (!(f = GetReadyForRead(fname))) {
return FALSE;
}
#if ITSTHEMODELLER
ResetStorage();
FileObjectId = -1;
#else
strcpy(n, fname);
SplitFileName(n,&path,&name);
if (!ReadingExternal) {
strcpy(FilePath, path);
strcpy(FileName, fname);
}
#endif
if (fgets(header,HeaderMax,f)) {
if (sscanf(header,"Compatibility: %f",&Compatibility) IS 1) {
if (Compatibility IS 1.00) { /* nb. There will never be any 1.00 files in the */
ReadModel100(f, fname); /* outside world, but we still have some. :) */
}
else if ((Compatibility >= 2.00) AND (Compatibility <= FileRevision)) {
GetByte(f, &type);
if ((type IS BinaryFile) OR (type IS ByteswappedFile)) {
byteswapped = (BigEndian() AND (type IS ByteswappedFile))
OR (!BigEndian() AND (type ISNT ByteswappedFile));
until (done OR !GetToken(f, &token)) {
switch (token) {
case BlockEnd: PatchReferences();
#if ITSTHEMODELLER
LoopThroughAllObjects(o) {
if (o->Id IS FileObjectId) {
*CurrentObject() = o;
break;
}
}
if (!o) { /* Not found... */
*CurrentObject() = *FirstObject();
}
#else
SearchForTextures();
#endif
done = TRUE; break;
case FMaterials: ReadMaterials(f); break;
case FTextureRef: ReadTextureRef(f, TRUE); break;
case FObject: ReadObject(f); break;
case FData: ReadDataItem(f); break;
/* Obsolete but needed for backwards compatibility */
case FTextureDir: ReadTextureDir(f); break;
case FTexture: ReadTextureRef(f, FALSE); break;
case FPush: ReadOldScene(f); break;
case FView: ReadView(f); break;
default: if (!SkipFuture(f)) {
SetError(BAD_FILE);
}
}
}
}
else {
SetError(UNKNOWN_FILE);
}
}
else {
SetError(BAD_REVISION);
}
}
else {
SetError(UNKNOWN_FILE);
}
}
else {
SetError(UNKNOWN_FILE);
}
FinishReading(f, Compatibility);
return !FileError;
}